package com.weilele.mvvm.view

import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.core.view.*
import com.weilele.mvvm.R
import com.weilele.mvvm.widget.BaseConstraintLayout
import com.weilele.mvvm.widget.BaseLinearLayout

/**
 * 监听子view无法消耗的竖向滑动位移
 */
open class ChildUnConsumedView : BaseConstraintLayout, NestedScrollingParent3 {

    /**
     * 已经变化了的值
     */
    private var unConsumedY = 0f
    private var unConsumedX = 0f
    private var swipeX = 0f
    private var swipeY = 0f

    /**
     * 是否还有惯性
     */
    private var hasFling = false

    /**
     * 设置开始拖拽的监听
     */
    private var canDragListener: ((child: View, target: View, axes: Int, type: Int) -> Boolean?)? =
        null

    /**
     * 当子view 不能下滑的时候，是否可以响应下滑监听
     * false :就是一个普通的 BaseLinearLayout
     */
    //下拉的时候，当子view不能消耗下拉，父view是否响应
    var canDragTop = true

    //向右滑动，当子view不能消耗下拉，父view是否响应
    var canDragStart = true

    //向左滑动
    var canDragEnd = true

    //上滑的时候，当子view不能消耗，父view是否响应
    var canDragBottom = true

    //不论何种情况总是可以嵌套滚动
    var isAlwaysEnableDrag = true

    //是否拦截惯性滚动
    var canInterceptFling = false

    constructor(context: Context) : this(context, null)
    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    ) {
        context.obtainStyledAttributes(attrs, R.styleable.ChildUnConsumedView).apply {
            canDragTop = getBoolean(R.styleable.ChildUnConsumedView_canDragTop, canDragTop)
            canDragStart = getBoolean(R.styleable.ChildUnConsumedView_canDragStart, canDragStart)
            canDragEnd = getBoolean(R.styleable.ChildUnConsumedView_canDragEnd, canDragEnd)
            canDragBottom = getBoolean(R.styleable.ChildUnConsumedView_canDragBottom, canDragBottom)
            isAlwaysEnableDrag =
                getBoolean(R.styleable.ChildUnConsumedView_isAlwaysEnableDrag, isAlwaysEnableDrag)
        }.recycle()
    }

    /**
     * 初始化
     */
    fun initAll() {
        unConsumedY = 0f
        unConsumedX = 0f
    }

    //停止滑动监听
    private val childUnConsumedListeners = mutableListOf<
            Function7<@ParameterName("parent") View/*父view*/,
                    @ParameterName("target") View/*子view*/,
                    @ParameterName("unConsumedX") Float/*移动距离*/,
                    @ParameterName("unConsumedY") Float/*移动距离*/,
                    @ParameterName("isScrolling") Boolean/*移动距离*/,
                    @ParameterName("hasFling") Boolean/*是否有惯性事件*/,
                    @ParameterName("type") Int/*是否是触摸触发的事件*/,
                    Unit>>()

    //滚动监听
    private val swipeListeners = mutableListOf<
            Function3<@ParameterName("dx") Float/*移动距离*/,
                    @ParameterName("dy") Float/*移动距离*/,
                    @ParameterName("isScrolling") Boolean/*移动距离*/,
                    Unit>>()

    /**
     * 添加监听
     */
    fun addOnChildUnConsumedListener(
        l: Function7<@ParameterName("parent") View/*父view*/,
                @ParameterName("target") View/*子view*/,
                @ParameterName("unConsumedX") Float/*移动距离*/,
                @ParameterName("unConsumedY") Float/*移动距离*/,
                @ParameterName("isScrolling") Boolean/*移动距离*/,
                @ParameterName("hasFling") Boolean/*是否有惯性事件*/,
                @ParameterName("type") Int/*是否是触摸触发的事件*/,
                Unit>
    ) {
        if (!childUnConsumedListeners.contains(l)) {
            childUnConsumedListeners.add(l)
        }
    }

    fun removeOnChildUnConsumedListener(
        l: Function7<@ParameterName("parent") View/*父view*/,
                @ParameterName("target") View/*子view*/,
                @ParameterName("unConsumedX") Float/*移动距离*/,
                @ParameterName("unConsumedY") Float/*移动距离*/,
                @ParameterName("isScrolling") Boolean/*移动距离*/,
                @ParameterName("hasFling") Boolean/*是否有惯性事件*/,
                @ParameterName("type") Int/*是否是触摸触发的事件*/,
                Unit>
    ) {
        childUnConsumedListeners.remove(l)
    }

    /**
     * 开始拖拽的监听
     */
    fun setOnStartCanDragListener(l: ((child: View, target: View, axes: Int, type: Int) -> Boolean?)?) {
        canDragListener = l
    }

    fun resetSwipe() {
        swipeX = 0f
        swipeY = 0f
    }

    /**
     * 添加手指滑动的距离监听
     */
    fun addOnSwipeListener(
        l: Function3<@ParameterName("swipeX") Float/*移动距离*/,
                @ParameterName("swipeY") Float/*移动距离*/,
                @ParameterName("isScrolling") Boolean/*移动距离*/,
                Unit>
    ) {
        if (!swipeListeners.contains(l)) {
            swipeListeners.add(l)
        }
    }

    /**
     * 移除手指滑动的距离监听
     */
    fun removeOnSwipeListener(
        l: Function3<@ParameterName("swipeX") Float/*移动距离*/,
                @ParameterName("swipeY") Float/*移动距离*/,
                @ParameterName("isScrolling") Boolean/*移动距离*/,
                Unit>
    ) {
        swipeListeners.remove(l)
    }

    /**
     * 清空监听
     */
    fun clearStopScrollListener() {
        childUnConsumedListeners.clear()
    }

    /**
     * 是否已经滚动到顶部
     */
    private fun hadScrollToTop(target: View): Boolean {
        //负表示是否可以向下滚动，
        //true则表示可以向下滚动，还没有滑动顶部
        //false 不可以向下滚动，已经滑动到顶部
        return !target.canScrollVertically(-1)
    }

    /**
     * 是否已经滚动到底部
     */
    private fun hadScrollToBottom(target: View): Boolean {
        return !target.canScrollVertically(1)
    }

    /**
     * 是否已经滚动到左边
     */
    private fun hadScrollToStart(target: View): Boolean {
        return !target.canScrollHorizontally(-1)
    }

    /**
     * 是否已经滚动到右边
     */
    private fun hadScrollToEnd(target: View): Boolean {
        return !target.canScrollHorizontally(1)
    }

    /******************************************Parent***********************************************/
    override fun onNestedScrollAccepted(child: View, target: View, axes: Int, type: Int) {

    }

    /**
     * child :直接子view
     * target：触发此方法的view，可能是子view的子view
     * axes：方向               {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
     *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL}
     * type:是否是触摸触发  TYPE_TOUCH, TYPE_NON_TOUCH
     */
    override fun onStartNestedScroll(child: View, target: View, axes: Int, type: Int): Boolean {
        //拦截惯性滚动
        if (canInterceptFling && type == ViewCompat.TYPE_NON_TOUCH) {
            return false
        }
        //优先给监听器处理
        val dragListener = canDragListener?.invoke(child, target, axes, type)
        if (dragListener != null) {
            return dragListener
        }
//        if (childUnConsumedListeners.isEmpty()) {
//            return false
//        }
        //四边都不允许拖动，直接返回false
        if (!canDragTop && !canDragBottom && !canDragStart && !canDragEnd) {
            return false
        }
        //不是触摸，直接返回 false
        //惯性滚动也会走这里，不做无脑拦截，可以通过canDragListener拦截
//        if (type == ViewCompat.TYPE_NON_TOUCH) {
//            return false
//        }
        //总是支持拖拽，直接返回true
        if (isAlwaysEnableDrag) {
            return true
        }
        //根据各边情况判断是否能接收嵌套滚动
        //比如 top view已经到达顶部，切支持拖拽则范湖true，否则false
        return (canDragTop && hadScrollToTop(target)) ||
                (canDragBottom && hadScrollToBottom(target)) ||
                (canDragStart && hadScrollToStart(target)) ||
                (canDragEnd && hadScrollToEnd(target))
    }

    /**
     * dy ：当前需要滑动总的距离
     * consumed 需要消耗的，剩下的分给孩子
     */
    override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {
        swipeX += dx
        swipeY += swipeY
        swipeListeners.forEach {
            it.invoke(swipeX, swipeY, true)
        }
        val oldChangeY = unConsumedY
        val oldChangeX = unConsumedX
        var isNeedCallListener = false
        //判断纵向滑动
        when {
            oldChangeY > 0 -> {
                val newY = oldChangeY - dy
                unConsumedY = if (newY < 0) {
                    consumed[1] = oldChangeY.toInt()
                    0f
                } else {
                    consumed[1] = dy
                    newY
                }
                isNeedCallListener = true
            }
            oldChangeY < 0 -> {
                val newY = oldChangeY - dy
                unConsumedY = if (newY > 0) {
                    consumed[1] = oldChangeY.toInt()
                    0f
                } else {
                    consumed[1] = dy
                    newY
                }
                isNeedCallListener = true
            }
            else -> {
                if ((canDragTop && dy < 0 && hadScrollToTop(target)) ||
                    (canDragBottom && dy > 0 && hadScrollToBottom(target))
                ) {
                    consumed[1] = dy
                    unConsumedY -= dy.toFloat()
                    isNeedCallListener = true
                }
            }
        }
        //判断横向滑动
        when {
            oldChangeX > 0 -> {
                val newX = oldChangeX - dx
                unConsumedX = if (newX < 0) {
                    consumed[0] = oldChangeX.toInt()
                    0f
                } else {
                    consumed[0] = dx
                    newX
                }
                isNeedCallListener = true
            }
            oldChangeX < 0 -> {
                val newX = oldChangeX - dx
                unConsumedX = if (newX > 0) {
                    consumed[0] = oldChangeX.toInt()
                    0f
                } else {
                    consumed[0] = dx
                    newX
                }
                isNeedCallListener = true
            }
            else -> {
                if ((canDragEnd && dx < 0 && hadScrollToStart(target)) ||
                    (canDragStart && dx > 0 && hadScrollToEnd(target))
                ) {
                    consumed[0] = dx
                    unConsumedX -= dx.toFloat()
                    isNeedCallListener = true
                }
            }
        }
        if (isNeedCallListener) {
            childUnConsumedListeners.forEach {
                it.invoke(
                    this, target, unConsumedX, unConsumedY, true, hasFling, type
                )
            }
        }
    }

    /**
     * dyConsumed:当前 子view需要消耗的值
     * dxUnconsumed：还没有消耗的值
     * consumed：可以消耗后，继续讲传递给上一层view
     */
    override fun onNestedScroll(
        target: View,
        dxConsumed: Int,
        dyConsumed: Int,
        dxUnconsumed: Int,
        dyUnconsumed: Int,
        type: Int,
        consumed: IntArray
    ) {
        super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed)
        //可以将子view未消耗完的，再分发，这里暂不这样处理
//        if (dyUnconsumed != 0) {
//            unConsumedY -= dyUnconsumed
//            childUnConsumedListeners.forEach {
//                it.invoke(this, target, unConsumedX, unConsumedY, true)
//            }
//        }
    }

    override fun onNestedScroll(
        target: View,
        dxConsumed: Int,
        dyConsumed: Int,
        dxUnconsumed: Int,
        dyUnconsumed: Int,
        type: Int
    ) {
        super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed)
    }

    /**
     * 返回值是否自己处理惯性事件
     * true :自己处理
     * false:系统处理，会模拟滑动
     * 默认交给系统处理
     */
    override fun onNestedPreFling(target: View, velocityX: Float, velocityY: Float): Boolean {
        if (canInterceptFling) {
            return true
        }
        hasFling = true
        return super.onNestedPreFling(target, velocityX, velocityY)
    }

    override fun onStopNestedScroll(target: View, type: Int) {
        swipeListeners.forEach {
            it.invoke(swipeX, swipeY, false)
        }
        if (unConsumedY != 0f || unConsumedX != 0f) {
            childUnConsumedListeners.forEach {
                it.invoke(this, target, unConsumedX, unConsumedY, false, hasFling, type)
            }
        }
        hasFling = false
    }
}